package dbmanager

import (
	"reflect"

	"gitee.com/night-tc/gobige/entity"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
)

/*
ModuleBase.go 文件定义了数据库管理模块的基类，实现了IServiceModel接口。
主要包括：
1. ModuleBase：管理模块的基础功能，包括数据加载、同步、初始化等操作。
2. 提供了从Redis和数据库加载数据的逻辑，支持同步加载和延时加载。
3. 支持数据保存、异常处理、模块加载完成后的回调等功能。
*/

/*
用于ModuleBase在SyncData里对单个数据进行生成数据的回调函数
issave 为true表示需要保存在DB；
*/
type GetMsgForFunc func(v IDataBaseModel, db *protomsg.DBDataModel) (issave bool)

var _ IServiceModel = &ModuleBase{}

/*
ModuleBase 实现了IServiceModel接口的基类，用于管理模块的基础功能。
*/
type ModuleBase struct {
	User          entity.IEntityReal    //用户对象
	Redisutil     *DataUtil             //redis操作
	IsLoad        bool                  //是否加载过
	Args          *DataArgs             //标脏收集器
	CallBreakFunc GetMsgForFunc         //用于ModuleBase在SyncData里对单个数据进行生成数据的回调函数
	IsSyncClient  bool                  //是否差量同步给客户端
	IsSyncSpace   bool                  //是否同步给Space
	LoadType      global.ServerTypeEnum //从哪里加载数据

	ServiceName string //服务器名字
}

// NewModuleBase 创建一个新的ModuleBase实例
func NewModuleBase(user entity.IEntityReal, Sname string) *ModuleBase {
	return &ModuleBase{
		User:      user,
		Redisutil: NewRedisUtilDBData(user.GetDBID()),
		IsLoad:    false,
		Args:      NewDataArgs(user.GetDBID()),
		CallBreakFunc: func(v IDataBaseModel, db *protomsg.DBDataModel) (issave bool) {
			return true
		},
		IsSyncClient: true,
		IsSyncSpace:  false,
		LoadType:     global.ServerTypeDefault,
		ServiceName:  Sname,
	}
}

//--------------------------------------------------------
/* 属性 */

// GetServiceName 返回系统模块的名称
func (this *ModuleBase) GetServiceName() string {
	return this.ServiceName
}

// GetTableNames 返回系统模块需要的表名列表
func (this *ModuleBase) GetTableNames() []string {
	return []string{}
}

// GetLoadServerType 返回从哪个服务器类型加载数据，ServerTypeDefault 表示同步从DB加载，其他结果都是延迟从其他服加载
func (this *ModuleBase) GetLoadServerType() global.ServerTypeEnum {
	return this.LoadType
}

// GetDataArgs 返回脏数据收集器
func (this *ModuleBase) GetDataArgs() *DataArgs {
	return this.Args
}

// IsSingleTable 判断是否为单一表加载，并返回是否有Limit，如果没有限制，Limit为-1
func (this *ModuleBase) IsSingleTable() (result bool, limit int32) {
	return false, -1
}

/* 属性 */
//-------------------------------------------------------
/* 数据填充逻辑 */

// LoadDB 从DB获取数据，或从别的服获取数据后的加载方法
func (this *ModuleBase) LoadDB(lis map[string][]IDataBaseModel) {
	/*
		代码例子
	*/

	// if spli, ok := lis[models.TableName]; ok {
	// 	for _, spmd := range spli {
	// 		ismd := spmd.(*models.ModelMD)
	// 		//赋值操作
	// 	}
	// }
	this.IsLoad = true
}

// LoadData 从Redis加载数据的方法
func (this *ModuleBase) LoadData(lis map[string][]IDataBaseModel) {
	/*
		代码例子
	*/
	// if spli, ok := lis[models.TableName]; ok {
	// 	for _, spmd := range spli {
	// 		ismd := spmd.(*models.ModelMD)
	// 		//赋值操作
	// 		this.IsLoad = true
	// 		return true
	// 	}
	// }
}

// GetIsLoaded 返回系统模块是否已经加载过数据
func (this *ModuleBase) GetIsLoaded() bool {
	return this.IsLoad

}

// OtherData 处理其他服务器同步过来的差量数据
func (this *ModuleBase) OtherData(msg *protomsg.DBUpUserDatasReq) {

}

// InitLoaded 在LoadDB和LoadData成功后调用，用于填充数据后的初始化
func (this *ModuleBase) InitLoaded() {

}

/* 数据填充逻辑 */
//-------------------------------------------------------
/* 同步数据逻辑 */

// SyncData 同步数据到客户端、Redis或DBServer
func (this *ModuleBase) SyncData() {
	/*
		一次循环，把数据保存，数据写入redis都做完了；
	*/
	if !this.Args.GetIsDirty() {
		return
	}
	args := this.Args
	dbmsg := args.GetMsgByForFunc(this.CallBreakFunc)

	//dbsever的发送
	if len(dbmsg.DBList)+len(dbmsg.DelList) > 0 {
		PostDBPlayer(this.User.GetDBID(), this.User.GetEntityID(), dbmsg)
	}
	this.SaveRedis(args.GetDirtyData(), dbmsg)
	args.Clear()

	// 同步给客户端
	if this.IsSyncClient {
		_ = this.User.Post(global.ServerTypeClient, dbmsg)
	}
	// 同步给场景服务器
	if this.IsSyncSpace {
		spmsg := new(protomsg.OtherUpUserDatasRet)
		spmsg.Data = dbmsg
		spmsg.ServiceName = this.GetServiceName()
		_ = this.User.Post(global.ServerTypeSpace, spmsg)
	}
	// 如果有其他服务器的话，同步给其他服务器

}

// AllDataSyncClient 将全量数据同步到客户端
func (this *ModuleBase) AllDataSyncClient() {
	if !this.IsLoad {
		return
	}
	msg := this.Args.GetMsgByAllData(nil)
	_ = this.User.Post(global.ServerTypeClient, msg)
}

/* 同步数据逻辑 */
//-------------------------------------------------------
/* REDIS操作 */

// GetRedisTableDataAllDoMD 从Redis获取指定表的所有数据
func (this *ModuleBase) GetRedisTableDataAllDoMD(tnames []string) (result map[string][]IDataBaseModel, err error) {
	result = make(map[string][]IDataBaseModel)
	for _, tname := range tnames {
		if li, err := this.GetRedisTableDataDoMD(tname); err != nil {
			return nil, err
		} else if len(li) == 0 {
			return nil, ErrNoData
		} else {
			result[tname] = li
		}
	}
	return result, nil
}

// GetRedisTableDataDoMD 从Redis中读数据，支持分字段的结构
func (this *ModuleBase) GetRedisTableDataDoMD(tname string) (result []IDataBaseModel, err error) {
	if li, err := this.Redisutil.RowsSingleGet(tname); err != nil {
		return nil, err
	} else {
		mg := GetDBSingleton()
		tmpli := make(map[uint64]reflect.Value, len(li))
		result = make([]IDataBaseModel, 0, len(li))
		var allfield bool
		var id uint64
		var field string
		for k, v := range li {
			/*
				尝试拿到UID，模块字段名字
				模块字段名字应该就是对应的反射名字
			*/

			if !allfield {
				id, field = this.Redisutil.GetKeySplitToFieldName(k)
				allfield = field == ""
			}
			if allfield {
				//如果没有字段名字那就是整行更新
				md := mg.GetTypeByTname(tname)
				if err = md.Unmarshal(v); err != nil {
					return nil, err
				}
				result = append(result, md)
				continue
			} else {
				//不然就是 单字段更新的逻辑
				var vmd reflect.Value
				var ok bool
				if vmd, ok = tmpli[id]; !ok {
					vmd = mg.GetValueByTname(tname).Elem()
					tmpli[id] = vmd
					result = append(result, vmd.Addr().Interface().(IDataBaseModel))
				}
				//拿到了数据
				fmd := vmd.FieldByName(field)
				if err = redisFullData(fmd, v); err != nil {
					return nil, err
				}
			}
		}
	}
	return
}

// SaveRedis 保存数据到redis
func (this *ModuleBase) SaveRedis(li map[string]IDataBaseModel, msg *protomsg.DBUpUserDatasReq) {
	this.SaveRedisDatas(li, msg.DBList)
	this.DelRedisDatas(li, msg.DelList)
}

// SaveRedisDatas Update 数据到redis
func (this *ModuleBase) SaveRedisDatas(li map[string]IDataBaseModel, dbli []*protomsg.DBDataModel) {
	sdli := make([]*protomsg.DBDataModel, 0, len(dbli))
	for _, msgdb := range dbli {
		md := li[msgdb.KeyName]
		if data, ok := md.(*MapModel); ok {
			this.SetRedisTableDataDoByte(data)
		} else {
			sdli = append(sdli, msgdb)
		}
	}
	if len(sdli) > 0 {
		if err := this.Redisutil.SetDatas(sdli); err != nil {
			logger.Errorf("SaveRedisDatas: SetDatas err:%s", err)
		}
	}
}

// SetRedisTableDataDoByte 写入redis，支持分字段的结构
func (this *ModuleBase) SetRedisTableDataDoByte(data *MapModel) {
	table, uid := GetKeyNameResolve(data.keyName)
	if err := this.Redisutil.RowsSingleSet(table, uid, data.Data); err != nil {
		logger.Warnf("SetRedisTableDataDoByte: RowsSingleSet err:%s", err)
	}
}

// DelRedisDatas 删redis里的数据
func (this *ModuleBase) DelRedisDatas(li map[string]IDataBaseModel, dbli []*protomsg.DBDataModel) {
	sdli := make([]*protomsg.DBDataModel, 0, len(dbli))
	for _, msgdb := range dbli {
		md := li[msgdb.KeyName]
		if md.GetIsPartial() {
			//分字段的数据
			fli := DBResolveModel(md)
			if submd, ok := md.(IPlayerSubBase); ok {
				this.Redisutil.RowsSingleDels(submd.TableName(), submd.GetID(), fli)
			}
		} else {
			sdli = append(sdli, msgdb)
		}
	}
	if len(sdli) > 0 {
		this.Redisutil.DelDataArray(sdli)
	}

}
